package kd.bos.openapi.sdk.auth.digestauth;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import kd.bos.openapi.common.result.CustomApiResult;
import kd.bos.openapi.common.util.StringUtil;
import kd.bos.openapi.sdk.util.DateUtil;
import kd.bos.openapi.sdk.util.HttpClientUtil;
import kd.bos.openapi.sdk.util.SignUtils;
import kd.bos.openapi.sdk.util.StringUtils;

import java.net.URLEncoder;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.logging.Logger;

/**
 * @author johnz
 * @Deacription 摘要认证工具类
 * @date 2023年10月23日
 */
public class OpenApiDigestAuthUtil {
    private final static Logger log = Logger.getLogger(OpenApiDigestAuthUtil.class.getName());
    // 摘要认证相关参数
    // 摘要随机数
    private static String DIGEST_PARAMS_SIGNATURE_NONCE = "signatureNonce";
    // 摘要时间戳
    private static String DIGEST_PARAMS_TIMESTAMP = "timestamp";
    // 摘要appId
    private static String DIGEST_PARAMS_APPID = "appId";
    // 摘要认证服务所在的数据中心
    private static String DIGEST_PARAMS_ACCOUNTID = "accountId";
    // 摘要认证用户信息，根据userType传值，userType is UserName, 就传用户的userName
    private static String DIGEST_PARAMS_USER = "user";
    // 摘要认证用户类型，userType
    private static String DIGEST_PARAMS_USERTYPE = "usertype";
    // 认证类型参数
    private static String DIGEST_PARAMS_OPENAPIAUTH = "OpenApiAuth";
    // 基本认证代理用户的secretKey
    private static String DIGEST_PARAMS_OPENAPISIGN = "openApiSign";
    // 摘要认证签名参数signature
    private static String DIGEST_PARAMS_SIGNATURE = "signature";
    // 请求内容类型
    private static String DIGEST_PARAMS_CONTENT_TYPE = "Content-Type";

    // userType：UserName
    private static String DIGEST_PARAMS_USER_TYPE_VALUE_USERNAME = "UserName";
    // 认证类型-摘要认证
    private static String DIGEST_PARAMS_AUTH_TYPE_VALUE_DIGEST = "2";
    // 认证类型-摘要认证
    private static String DIGEST_PARAMS_CONTENT_TYPE_VALUE = "application/json";


    /**
     * 摘要认证发送请求方法
     *
     * @param url     摘要认证请求URL
     * @param params  摘要认证相关参数
     * @param clazz   返回类型
     * @param content 请求内容
     * @param <T>     返回类型
     * @return 请求响应内容
     * @throws Exception
     */
    public static <T> CustomApiResult<T> sendDigestGetApiRequest(String url, DigestParamDto params, Class<T> clazz, String content) throws Exception {
        String appSecret = params.getPublicKey();
        Map<String, String> headerMap = new HashMap<>();
        String timestamp = DateUtil.format(new Date(), SignUtils.SIGNATUR_TIME_FORMAT);
        String uuid = UUID.randomUUID().toString();

        StringBuilder urlBuilder = new StringBuilder(url);
        if (StringUtils.isEmpty(params.getSecretKey())) {
            urlBuilder.append("?appId=").append(params.getAppId());
            urlBuilder.append("&user=").append(params.getUser());
            urlBuilder.append("&usertype=").append(params.getUserType());
        } else {
            urlBuilder.append("?").append(DIGEST_PARAMS_OPENAPISIGN).append("=").append(params.getSecretKey());
            headerMap.put("openapiauth","2");
        }
        urlBuilder.append("&signatureNonce=").append(uuid);
        urlBuilder.append("&timestamp=").append(URLEncoder.encode(timestamp));
        String sb = content + timestamp + uuid;
        String signature = SignUtils.HMACSHA256StrByKey(sb, appSecret);

        urlBuilder.append("&").append(SignUtils.SIGNATUR_PARAMS).append("=").append(signature);
        urlBuilder.append("&").append(content);
        urlBuilder.append("&").append("parameters").append("=").append(params.getParameters());

        if (params.isPrint()) {
            log.info("------------------------sendDigestGetApiRequest request.url--------------------------------------------");
            log.info(urlBuilder.toString());
        }
        headerMap.put(DIGEST_PARAMS_CONTENT_TYPE, DIGEST_PARAMS_CONTENT_TYPE_VALUE);
        String response = "";
        try {
            response = HttpClientUtil.get(urlBuilder.toString(), headerMap, null);
        } catch (Exception e) {
            String message = "request.header:" + JSON.toJSONString(headerMap)
                    + " request.body:" + content + " response-=" + response + " sendDigestGetApiRequest send post error:" + e.getMessage();
            log.info(message);
            throw new Exception(message + e.getMessage(), e);
        }
        if (response.contains("login.loginBizException")) {
            JSONObject errorResponse = JSON.parseObject(response);
            String message = "sendDigestApiRequest error:" + errorResponse.getString("errorMsg");
            log.info(message);
            throw new Exception(response);
        }
        CustomApiResult<T> result;
        try {
            result = JSON.parseObject(response, new TypeReference<CustomApiResult<T>>() {
            });
            String dataString = JSON.toJSONString(result.getData());
            T data = JSON.toJavaObject(JSON.parseObject(dataString), clazz);
            result.setData(data);
        } catch (Exception e) {
            throw new Exception("receive post error:" + e.getMessage(), e);
        }
        return result;
    }

    /**
     * 摘要认证发送请求方法
     *
     * @param url         摘要认证请求URL
     * @param params      摘要认证相关参数
     * @param clazz       返回类型
     * @param jsonContent 请求内容
     * @param <T>         返回类型
     * @return 请求响应内容
     * @throws Exception
     */
    public static <T> CustomApiResult<T> sendDigestApiRequest(String url, DigestParamDto params, Class<T> clazz, String jsonContent) throws Exception {
        String appSecret = params.getPublicKey();
        String openApiSign = params.getSecretKey();
        Map<String, String> headerMap = new HashMap<>();
        String timestamp = DateUtil.format(new Date(), SignUtils.SIGNATUR_TIME_FORMAT);
        String uuid = UUID.randomUUID().toString();
        headerMap.put(DIGEST_PARAMS_SIGNATURE_NONCE, uuid);
        headerMap.put(DIGEST_PARAMS_TIMESTAMP, timestamp);
        if (StringUtil.isEmpty(openApiSign)) {
            headerMap.put(DIGEST_PARAMS_APPID, params.getAppId());
            if (params.getAccountId() != null) {
                headerMap.put(DIGEST_PARAMS_ACCOUNTID, params.getAccountId());
            }
            headerMap.put(DIGEST_PARAMS_USER, params.getUser());
            headerMap.put(DIGEST_PARAMS_USERTYPE, params.getUserType());
        } else {
            headerMap.put(DIGEST_PARAMS_OPENAPISIGN, openApiSign);
            headerMap.put(DIGEST_PARAMS_OPENAPIAUTH, params.getAuthType());
        }
        String sb = jsonContent + timestamp + uuid;
        String signature = SignUtils.HMACSHA256StrByKey(sb, appSecret);

        headerMap.put(DIGEST_PARAMS_SIGNATURE, signature);
        headerMap.put(DIGEST_PARAMS_CONTENT_TYPE, DIGEST_PARAMS_CONTENT_TYPE_VALUE);

        if (params.isPrint()) {
            log.info("------------------------sendDigestApiRequest request.url--------------------------------------------");
            log.info(url);

            log.info("------------------------sendDigestApiRequest request.header--------------------------------------------");
            log.info(JSON.toJSONString(headerMap, true));

            log.info("------------------------sendDigestApiRequest request.body--------------------------------------------");
            log.info(jsonContent);
        }

        String response = "";
        try {
            response = HttpClientUtil.post(url, headerMap, jsonContent);
        } catch (Exception e) {
            String message = "request.header:" + JSON.toJSONString(headerMap)
                    + " request.body:" + jsonContent + " response-=" + response + " sendDigestApiRequest send post error:" + e.getMessage();
            log.info(message);
            throw new Exception(message + e.getMessage(), e);
        }
        if (response.contains("login.loginBizException")) {
            JSONObject errorResponse = JSON.parseObject(response);
            String message = "sendDigestApiRequest error:" + errorResponse.getString("errorMsg");
            log.info(message);
            throw new Exception(response);
        }
        CustomApiResult<T> result;
        try {
            result = JSON.parseObject(response, new TypeReference<CustomApiResult<T>>() {
            });
            String dataString = JSON.toJSONString(result.getData());
            T data = JSON.toJavaObject(JSON.parseObject(dataString), clazz);
            result.setData(data);
        } catch (Exception e) {
            throw new Exception("receive post error:" + e.getMessage(), e);
        }
        return result;
    }


}
